﻿/*
 * ColorByQuantities
 * 
 * initial implementation : 20.10.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Collections.Generic;
using System.Drawing;
using Petra.Gis.RenderingServices.PlanarBase;

namespace Petra.Gis.RenderingServices.GDIPlus
{

  public class ColorByQuantities : SmartStyleComponent
  {

    protected double tmpVal;

    //protected double _lowNumber;
    //protected double _highNumber;



    public ColorByQuantities()
    { 
      //_definitions = new Dictionary<object,Color>();
    }





    protected string _sourceAttributeName;

    public string SourceAttributeName
    {
      get { return _sourceAttributeName; }
      set { _sourceAttributeName = value; }
    }





    protected QuantificationModelType _quantificationModel;


    /// <summary>
    /// Gets or sets how the color changes are distributed
    /// </summary>
    public virtual QuantificationModelType QuantificationType
    {
      get { return _quantificationModel; }
      set { _quantificationModel = value; }
    }





    protected double _highValue;


    /// <summary>
    /// Gets or sets the high value for this colorbreak.
    /// </summary>
    public virtual double HighValue
    {
      // These are overridden in the subclass and T type variables are used instead
      get{ return _highValue; }
      set
      {

        //if (value <= _lowValue)
        //  throw new ArgumentException("high must be higher then low");

        _highValue = value; // set this regardless

      }

    }



    protected double _lowValue;
    
    /// <summary>
    /// Gets or sets the lower value for the break.  
    /// </summary>
    public virtual double LowValue
    {
      // These are overridden so that these private variables are only used if a generic ColorBreak is NOT being used.
      get { return _lowValue; }
      set
      {
        _lowValue = value;
      }
    }


    

    protected Color _lowColor;

    /// <summary>
    /// Gets or sets the color to be used for this break.  For
    /// BiValued breaks, this only sets one of the colors.  If
    /// this is higher than the high value, both are set to this.
    /// If this equals the high value, IsBiValue will be false.
    /// </summary>
    public virtual Color LowColor
    {
      get { return _lowColor; }
      set { _lowColor = value; }
    }





    protected Color _highColor;


    /// <summary>
    /// Gets or sets the second of two colors to be used.
    /// This is only used for BiValued breaks.  
    /// </summary>
    public virtual Color HighColor
    {
      get { return _highColor; }
      set { _highColor = value; }
    }






    /// <summary>
    /// calculates color based on input value given by renderer from data
    /// </summary>
    /// <param name="value">The value to be converted into a color from the range on this colorbreak</param>
    /// <returns>A color that is selected from the range values.</returns>
    public override object GetComponentByValue(object value)
    {

      // try convert to double
      try
      {
        tmpVal = System.Convert.ToDouble(value);
      }
      catch { return Color.Transparent; }

      if (!Contains(tmpVal))
        return Color.Transparent;

      if (tmpVal == _lowValue)
        return _lowColor;

      double Range = _highValue - _lowValue;

      double p = 0; // the portion of the range, where 0 is LowValue & 1 is HighValue
      double ht;
      switch (_quantificationModel)
      {
        case QuantificationModelType.Linear:
          p = (tmpVal - _lowValue) / Range;
          break;
        case QuantificationModelType.Exponential:
          ht = tmpVal;
          if (ht < 1) ht = 1.0;
          if (Range > 1)
          {
            p = (Math.Pow(ht - _lowValue, 2) / Math.Pow(Range, 2));
          }
          else
          {
            return LowColor;
          }
          break;
        case QuantificationModelType.Logarithmic:
          ht = tmpVal;
          if (ht < 1) ht = 1.0;
          if (Range > 1.0 && ht - _lowValue > 1.0)
          {
            p = Math.Log(ht - _lowValue) / Math.Log(Range);
          }
          else
          {
            return LowColor;
          }
          break;
      }

      int alpha = ByteRange(LowColor.A + Math.Round((HighColor.A - LowColor.A) * p));
      int red = ByteRange(LowColor.R + Math.Round((HighColor.R - LowColor.R) * p));
      int green = ByteRange(LowColor.G + Math.Round((HighColor.G - LowColor.G) * p));
      int blue = ByteRange(LowColor.B + Math.Round((HighColor.B - LowColor.B) * p));
      return Color.FromArgb(alpha, red, green, blue);

    }






    /// <summary>
    /// Tests to see if the specified value falls in the range
    /// </summary>
    /// <param name="value">The value to test</param>
    /// <returns>Boolean, true if the value was found in the range</returns>
    protected virtual bool Contains(double value)
    {

      return (value >= _lowValue && value <= _highValue);

    }





    /// <summary>
    /// Returns an integer that ranges from 0 to 255.  If value is larger than 255, the value will be equal to 255.
    /// If the value is smaller than 255, it will be equal to 255.
    /// </summary>
    /// <param name="value">A Double value to convert.</param>
    /// <returns>An integer ranging from 0 to 255</returns>
    protected int ByteRange(double value)
    {
      int rounded = (int)Math.Round(value);
      if (rounded > 255) return 255;
      if (rounded < 0) return 0;
      return rounded;
    }

  }
}
